大文件断点续传如何实现? |
您所在的位置:网站首页 › oss browser上传csv文件大小限制 › 大文件断点续传如何实现? |
苏三说技术 2023-03-08 08:20 发表于四川 以下文章来源于阿Q说代码 ,作者阿Q 阿Q说代码. 专注于后端技术栈分享:文章风格多变、配图通俗易懂、故事生动有趣 java突击队 技术经验分享 公众号 各位看官大家好,今天给大家分享的又是一篇实战文章,希望大家能够喜欢。 开味菜最近接到一个新的需求,需要上传2G左右的视频文件,用测试环境的OSS试了一下,上传需要十几分钟,再考虑到公司的资源问题,果断放弃该方案。 一提到大文件上传,我最先想到的就是各种网盘了,现在大家都喜欢将自己收藏的「小电影」上传到网盘进行保存。网盘一般都支持断点续传和文件秒传功能,减少了网络波动和网络带宽对文件的限制,大大提高了用户体验,让人爱不释手。 说到这,大家先来了解一下这几个概念: 「文件分块」:将大文件拆分成小文件,将小文件上传\下载,最后再将小文件组装成大文件; 「断点续传」:在文件分块的基础上,将每个小文件采用单独的线程进行上传\下载,如果碰到网络故障,可以从已经上传\下载的部分开始继续上传\下载未完成的部分,而没有必要从头开始上传\下载; 「文件秒传」:资源服务器中已经存在该文件,其他人上传时直接返回该文件的URI。 RandomAccessFile平时我们都会使用FileInputStream,FileOutputStream,FileReader以及FileWriter等IO流来读取文件,今天我们来了解一下RandomAccessFile。 它是一个直接继承Object的独立的类,底层实现中它实现的是DataInput和DataOutput接口。该类支持随机读取文件,随机访问文件类似于文件系统中存储的大字节数组。 它的实现基于「文件指针」(一种游标或者指向隐含数组的索引),文件指针可以通过getFilePointer方法读取,也可以通过seek方法设置。 输入时从文件指针开始读取字节,并使文件指针超过读取的字节,如果写入超过隐含数组当前结尾的输出操作会导致扩展数组。该类有四种模式可供选择: r: 以只读方式打开文件,如果执行写入操作会抛出IOException; rw: 以读、写方式打开文件,如果文件不存在,则尝试创建文件; rws: 以读、写方式打开文件,要求对文件内容或元数据的每次更新都同步写入底层存储设备; rwd: 以读、写方式打开文件,要求对文件内容的每次更新都同步写入底层存储设备; 在rw模式下,默认是使用buffer的,只有cache满的或者使用RandomAccessFile.close()关闭流的时候才真正的写到文件。 API 1、void seek(long pos):设置下一次读取或写入时的文件指针偏移量,通俗点说就是指定下次读文件数据的位置。 ❝偏移量可以设置在文件末尾之外,只有在偏移量设置超出文件末尾后,才能通过写入更改文件长度; ❞2、native long getFilePointer():返回当前文件的光标位置; 3、native long length():返回当前文件的长度; 4、「读」方法 5、「写」方法 6、readFully(byte[] b):这个方法的作用就是将文本中的内容填满这个缓冲区b。如果缓冲b不能被填满,那么读取流的过程将被阻塞,如果发现是流的结尾,那么会抛出异常; 7、FileChannel getChannel():返回与此文件关联的唯一FileChannel对象; 8、int skipBytes(int n):试图跳过n个字节的输入,丢弃跳过的字节; ❝RandomAccessFile的绝大多数功能,已经被JDK1.4的NIO的「内存映射」文件取代了,即把文件映射到内存后再操作,省去了频繁磁盘io。 ❞ 主菜总结经验,砥砺前行:之前的实战文章中过多的粘贴了源码,影响了各位小伙伴的阅读感受。经过大佬的点拨,以后将展示部分关键代码,供各位赏析,源码可在「后台」获取。 文件分块文件分块需要在前端进行处理,可以利用强大的js库或者现成的组件进行分块处理。需要确定分块的大小和分块的数量,然后为每一个分块指定一个索引值。 为了防止上传文件的分块与其它文件混淆,采用文件的md5值来进行区分,该值也可以用来校验服务器上是否存在该文件以及文件的上传状态。 如果文件存在,直接返回文件地址; 如果文件不存在,但是有上传状态,即部分分块上传成功,则返回未上传的分块索引数组; 如果文件不存在,且上传状态为空,则所有分块均需要上传。 fileRederInstance.readAsBinaryString(file); fileRederInstance.addEventListener("load", (e) => { let fileBolb = e.target.result; fileMD5 = md5(fileBolb); const formData = new FormData(); formData.append("md5", fileMD5); axios .post(http + "/fileUpload/checkFileMd5", formData) .then((res) => { if (res.data.message == "文件已存在") { //文件已存在不走后面分片了,直接返回文件地址到前台页面 success && success(res); } else { //文件不存在存在两种情况,一种是返回data:null代表未上传过 一种是data:[xx,xx] 还有哪几片未上传 if (!res.data.data) { //还有几片未上传情况,断点续传 chunkArr = res.data.data; } readChunkMD5(); } }) .catch((e) => {}); });在调用上传接口前,通过slice方法来取出索引在文件中对应位置的分块。 const getChunkInfo = (file, currentChunk, chunkSize) => { //获取对应下标下的文件片段 let start = currentChunk * chunkSize; let end = Math.min(file.size, start + chunkSize); //对文件分块 let chunk = file.slice(start, end); return { start, end, chunk }; };之后调用上传接口完成上传。 断点续传、文件秒传后端基于spring boot开发,使用redis来存储上传文件的状态和上传文件的地址。 如果文件完整上传,返回文件路径;部分上传则返回未上传的分块数组;如果未上传过返回提示信息。 ❝在上传分块时会产生两个文件,一个是文件主体,一个是临时文件。临时文件可以看做是一个数组文件,为每一个分块分配一个值为127的字节。 ❞校验MD5值时会用到两个值: 文件上传状态:只要该文件上传过就不为空,如果完整上传则为true,部分上传返回false; 文件上传地址:如果文件完整上传,返回文件路径;部分上传返回临时文件路径。 /** * 校验文件的MD5 **/ public Result checkFileMd5(String md5) throws IOException { //文件是否上传状态:只要该文件上传过该值一定存在 Object processingObj = stringRedisTemplate.opsForHash().get(UploadConstants.FILE_UPLOAD_STATUS, md5); if (processingObj == null) { return Result.ok("该文件没有上传过"); } boolean processing = Boolean.parseBoolean(processingObj.toString()); //完整文件上传完成时为文件的路径,如果未完成返回临时文件路径(临时文件相当于数组,为每个分块分配一个值为127的字节) String value = stringRedisTemplate.opsForValue().get(UploadConstants.FILE_MD5_KEY + md5); //完整文件上传完成是true,未完成返回false if (processing) { return Result.ok(value,"文件已存在"); } else { File confFile = new File(value); byte[] completeList = FileUtils.readFileToByteArray(confFile); List missChunkList = new LinkedList(); for (int i = 0; i |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |